package com.qinggeng.project.api.controller;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.qinggeng.common.constant.Constants;
import com.qinggeng.common.enums.ShowTypeEnum;
import com.qinggeng.common.exception.CustomException;
import com.qinggeng.common.utils.DateUtils;
import com.qinggeng.framework.redis.RedisCache;
import com.qinggeng.framework.web.controller.BaseController;
import com.qinggeng.framework.web.domain.AjaxResult;
import com.qinggeng.project.api.dto.*;
import com.qinggeng.project.api.security.annotation.LoginRequired;
import com.qinggeng.project.api.security.utils.ApiUtils;
import com.qinggeng.project.system.domain.Goods;
import com.qinggeng.project.system.domain.SysUser;
import com.qinggeng.project.system.domain.UserBeat;
import com.qinggeng.project.system.domain.UserBeatHelp;
import com.qinggeng.project.system.service.*;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.*;


/**
 * @author 吴鹏飞
 * @date 2020/10/27
 */

@Api(tags = "捶一捶模块")
@RestController
@Slf4j
@CrossOrigin
@RequestMapping("${apiPath}/beat")
public class ApiBeatABeatController extends BaseController {
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private IGoodsService goodsService;

    @Autowired
    private ISysConfigService configService;

    @Autowired
    private UserBeatService userBeatService;

    @Autowired
    private IOrdersService ordersService;
    @Resource
    private RedissonClient redissonClient;

    @Autowired
    private UserBeatHelpService userBeatHelpService;


    @ApiOperation("获取捶一捶活动信息")
    @GetMapping("/beatInformation")
    @ApiResponses({
            @ApiResponse(code = 200, message = "", reference = "open 1开启 0关闭 img 图 returnUrl 跳转链接 icon")
    })
    public AjaxResult beatInformation() {
        try {
            //获取活动信息
            String data = configService.selectConfigByKey("sys.activity.beat").toString();
            //解析
            JSONObject beatConfig = JSONObject.parseObject(data);

            Map<String, String> map = Maps.newHashMap();
            //1开启 0关闭
            map.put("open", beatConfig.getString("open"));
            map.put("img", beatConfig.getString("img"));
            map.put("returnUrl", beatConfig.getString("returnUrl"));
            map.put("buttonName", beatConfig.getString("buttonName"));
            map.put("icon", beatConfig.getString("icon"));
            return AjaxResult.success(map);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("获取捶一捶活动信息" + e.getMessage());
            return AjaxResult.error(e.getMessage());
        }
    }

    @ApiOperation("活动规则")
    @GetMapping("/activityRule")
    public AjaxResult activityRule() {
        try {
            //获取活动规则
            String data = configService.selectConfigByKey("sys.beat.rule").toString();
            //解析
            JSONObject beatConfig = JSONObject.parseObject(data);
            String rule = beatConfig.getString("rule");
            Map<String, String> map = Maps.newHashMap();

            map.put("rule", rule);
            return AjaxResult.success("获取成功", map);
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.error(e.getMessage());
        }
    }


    @ApiOperation("查询所有捶一捶商品")
    @GetMapping("/beatGoodsList")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "goodsType", value = "商品类型 40=捶一捶", dataType = "String", paramType = "query", required = true),
            @ApiImplicitParam(name = "shopId", value = "商户id", dataType = "long", paramType = "query", required = true),
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "", response = GoodsDto.class)
    })
    @LoginRequired(role = ApiUtils.DEFAULT_ROLE_USER)
    public AjaxResult beatGoodsList(String goodsType, Long shopId) {
        try {
            startPage();
            Goods goods = new Goods();
            goods.setGoodsType(goodsType);
            if (shopId == null) {
                return AjaxResult.error("商家不存在，shopId=" + shopId);
            }
            //固定商家为我们 6
            goods.setShopId(shopId);
            List<GoodsDto> goodsDtoList = goodsService.selectGoodsListDto(goods);
            //根据用户id查询未过期正在捶的商品id集合
            List<Long> mainCategorys = userBeatService.selectMainCategorysByNowAndUserId(ApiUtils.getUserId());

            for (GoodsDto goodsDto : goodsDtoList) {
                //遍历判断当前用户是否捶过此商品
                if (mainCategorys.contains(goodsDto.getMainCategory())) {
                    goodsDto.setIsBeat(true);
                }
            }
            //打乱顺序
            Collections.shuffle(goodsDtoList);
            return AjaxResult.success(goodsDtoList);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("查询所有捶一捶商品" + e.getMessage());
            return AjaxResult.error(e.getMessage());
        }
    }

    @ApiOperation("开捶")
    @GetMapping("/beatGoods")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ordersId", value = "订单Id", dataType = "Long", paramType = "query", required = true),
            @ApiImplicitParam(paramType = "header", dataType = "String", name = Constants.TOKEN, value = "token标记", required = true)
    })
    @LoginRequired(role = ApiUtils.DEFAULT_ROLE_USER)
    @Transactional
    public AjaxResult beatGoods(Long ordersId) {
        RLock lock = redissonClient.getLock("BEATGOODS_LOCK_" + ordersId);
        lock.lock();
        try {
            UserBeat userBeat = userBeatService.selectUserBeatByOrdersId(ordersId);
            if (userBeat == null) {
                return AjaxResult.error("此捶一捶订单不存在，ordesId=" + ordersId, ShowTypeEnum.用户.getValue());
            }
            if (!userBeat.getUserId().equals(ApiUtils.getUserId())) {
                //查询当天帮捶的次数,自己捶的商品除外
                List<UserBeatHelp> userBeatHelps = userBeatHelpService.selectUserBeatHelpByUserIdAndNow(ApiUtils.getUserId());
                if (userBeatHelps != null) {
                    JSONObject otherParams = JSONObject.parseObject(configService.selectConfigByKey("sys.beat.data").toString());
                    if (userBeatHelps.size() >= Integer.parseInt(otherParams.getString("helpCount"))) {
                        return AjaxResult.error("今天帮捶次数已用完,请明天再来哦", ShowTypeEnum.用户.getValue());
                    }
                }
            }
            //获取所有需要捶的金额集合
            List<String> random = redisCache.getCacheList("ORDERS_ID:" + ordersId);
            if (random == null) {
                return AjaxResult.error("还未申请捶一捶商品", ShowTypeEnum.用户.getValue());
            }

            //判断商品是否捶成
            if (random.size() == 0) {
                return AjaxResult.error("此商品已捶成", ShowTypeEnum.用户.getValue());
            }

            //查询所有捶过此订单的用户
            List<HelpRecordDto> helpRecordDtos = redisCache.getCacheList("ORDERS_HELP:" + ordersId);
            if (helpRecordDtos != null && helpRecordDtos.size() > 0) {
                for (HelpRecordDto helpRecordDto : helpRecordDtos) {
                    //判断当前用户是否在其中
                    if (helpRecordDto.getUserId().equals(ApiUtils.getUserId())) {
                        return AjaxResult.error("您已捶过，谢谢您的参与。", ShowTypeEnum.用户.getValue());
                    }
                }
            }

            //获取捶价并去除此价格
            String beatPrice=userBeatHelpService.getUserBeatPrice(random,userBeat);

            BigDecimal percent = null;
            Date date = null;
            BigDecimal remainAmount = null;
            if ((random.size() + 1) > 0) {
                //获取过期时间
                Long time = redisCache.getExpire("ORDERS_ID:" + ordersId);
                //获取过期日期
                date = new Date(Instant.now().toEpochMilli() + time * 1000);
                //添加帮捶记录
                helpRecord(ApiUtils.getApiUser().getUser(), userBeat, beatPrice, date);

                //删除redis存的旧的数组
                redisCache.deleteObject("ORDERS_ID:" + ordersId);
                //重新添加集合入redis
                Collections.reverse(random);
                redisCache.setCacheList("ORDERS_ID:" + ordersId, random);
                //重新设置过期时间
                redisTemplate.expireAt("ORDERS_ID:" + ordersId, date);

                //获取总价格
                BigDecimal price = (BigDecimal) redisTemplate.opsForValue().get("ORDERS_PRICE:" + ordersId);
                //求剩余总金额
                BigDecimal sum = new BigDecimal("0");
                for (String s : random) {
                    sum = sum.add(new BigDecimal(s));
                }
                remainAmount = sum;
                //计算剩余比例
                percent = sum.divide(price, 1, BigDecimal.ROUND_HALF_UP);
            }

            //更新订单捶成时间
            if (random.size() == 0) {
                userBeatService.updateUserBeatByOrdersId(ordersId);
            }
            HashMap<String, String> data = Maps.newHashMap();
            //砍的价格
            data.put("beatPrice", beatPrice);
            //剩余次数
            data.put("count", random.size() + "");
            //剩余比例
            data.put("percent", random.size() == 0 ? (0 + "") : (percent + ""));
            //截止时间
            data.put("time", date != null ? date.getTime() + "" : "");
            //剩余金额
            data.put("remainAmount", random.size() == 0 ? (0 + "") : remainAmount + "");
            return AjaxResult.success(data);
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            e.printStackTrace();
            log.error("开捶" + e.getMessage());
            return AjaxResult.error("系统异常:" + e.getMessage());
        } finally {
            lock.unlock();
        }
    }

    @ApiOperation("随机捶一捶商品")
    @GetMapping("/randomBeatGoods")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "shopId", value = "商户Id", dataType = "Long", paramType = "query", required = true),
            @ApiImplicitParam(paramType = "header", dataType = "String", name = Constants.TOKEN, value = "token标记", required = true)
    })
    @LoginRequired(role = ApiUtils.DEFAULT_ROLE_USER)
    public AjaxResult randomGoods(Long shopId) {
        try {
            Goods goods = new Goods();
            //设置商户Id
            goods.setShopId(shopId);
            //设置商品类型为捶一捶商品
            goods.setGoodsType("40");
            List<GoodsDto> goodsList = goodsService.selectGoodsListDto(goods);
            //随意抽取一个
            int randomInt = new Random().nextInt(goodsList.size());
            GoodsDto goodsDto = goodsList.get(randomInt);
            return AjaxResult.success(goodsDto);
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.error(e.getMessage());
        }

    }

    @ApiOperation("领取商品")
    @GetMapping("/receiveGoods")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ordersId", value = "订单Id", dataType = "Long", paramType = "query", required = true),
            @ApiImplicitParam(paramType = "header", dataType = "String", name = Constants.TOKEN, value = "token标记", required = true)
    })
    @LoginRequired(role = ApiUtils.DEFAULT_ROLE_USER)
    public AjaxResult receiveGoods(Long ordersId) {
        try {

            boolean b = userBeatService.receiveGoods(ordersId);
            if (b==false){
                throw new CustomException("领取失败");
            }
            //删除key
            redisCache.deleteObject("ORDERS_ID:" + ordersId);
            redisCache.deleteObject("ORDERS_PRICE:" + ordersId);
            redisCache.deleteObject("ORDERS_HELP:" + ordersId);
            return AjaxResult.success();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("领取商品" + e.getMessage());
            return AjaxResult.error(e.getMessage());
        }
    }


    @ApiOperation("获取已捶订单列表")
    @GetMapping("/getbeatGoodsList")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "beatIndex", value = "1=用户捶一捶进行中 2=用户捶成功且未领取 ", dataType = "String", required = true),
            @ApiImplicitParam(paramType = "header", dataType = "String", name = Constants.TOKEN, value = "token标记", required = true),
    })
    @LoginRequired(role = ApiUtils.DEFAULT_ROLE_USER)
    public AjaxResult getbeatGoodsList(String beatIndex) {
        try {
            startPage();
            if (!"1".equals(beatIndex)) {
                AjaxResult.error("此条件不存在,beatIndex = " + beatIndex);
            }

            UserBeatDto userBeatDto = new UserBeatDto();
            //1=用户捶一捶进行中或完成但未领取
            userBeatDto.setBeatIndex(beatIndex);
            //进行锤子用户
            userBeatDto.setUserId(ApiUtils.getUserId());

            //查出记录所有结果
            List<UserBeatDto> userBeatDtos = userBeatService.selectUserBeatList(userBeatDto);
            //判断是否捶过第一捶
            if (userBeatDtos != null && userBeatDtos.size() > 0) {
                if (beatIndex.equals("1")) {
                    for (UserBeatDto beatDto : userBeatDtos) {
                        //获取redis集合,判断是否相等
                        List<String> random = redisCache.getCacheList("ORDERS_ID:" + beatDto.getOrdersId());
                        BigDecimal goodsPrice = redisCache.getCacheObject("ORDERS_PRICE:" + beatDto.getOrdersId());
                        //获取过期时间
                        Long time = redisCache.getExpire("ORDERS_ID:" + beatDto.getOrdersId());
                        //获取过期日期
                        Date date = new Date(Instant.now().toEpochMilli() + time * 1000);
                        //获取剩余总金额
                        BigDecimal sum = new BigDecimal("0");
                        for (String s : random) {
                            sum = sum.add(new BigDecimal(s));
                        }
                        BigDecimal totalBeatPrice = goodsPrice.subtract(sum);
                        beatDto.setTotalBeatPrice(totalBeatPrice.toString());
                        beatDto.setCount(random.size());
                        beatDto.setPercent(random.size() == 0 ? 0 + "" : sum.divide(goodsPrice, 1, BigDecimal.ROUND_HALF_UP).toString());
                        beatDto.setDate(date.getTime() + "");
                        beatDto.setRemainAmount(random.size() == 0 ? 0 + "" : sum + "");
                        if (beatDto.getBeatSum() == random.size()) {
                            //未捶过
                            beatDto.setFirstBeat("0");
                        } else {
                            //捶过
                            beatDto.setFirstBeat("1");
                        }
                    }
                }

                if (beatIndex.equals("2")) {
                    for (UserBeatDto beatDto : userBeatDtos) {
                        beatDto.setTotalBeatPrice("全额");
                        beatDto.setCount(0);
                        beatDto.setPercent(0 + "");
                        beatDto.setDate("");
                        beatDto.setRemainAmount(0 + "");
                    }
                }
            }
            if (userBeatDtos == null) {
                //给一个空数组
                userBeatDtos = new ArrayList<>();
            }
            return AjaxResult.success(userBeatDtos);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("获取已捶订单列表" + e.getMessage());
            return AjaxResult.error(e.getMessage());
        }
    }

    @ApiOperation("获取捶一捶订单信息")
    @GetMapping("/getBeatOrdersInfo")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = Constants.TOKEN, value = "token标记", required = true),
            @ApiImplicitParam(paramType = "ordersId", value = "订单id", dataType = "Long", required = true),
    })
    @LoginRequired(role = ApiUtils.DEFAULT_ROLE_USER)
    public AjaxResult getBeatOrdersInfo(Long ordersId) {
        try {
            //查询用户捶一捶信息和用户信息
            BeatOrdersInfoDto beatOrdersInfoDto = userBeatService.selectUserBeatAndUserInfoByOrdersId(ordersId);
            if (beatOrdersInfoDto == null) {
                return AjaxResult.error("此订单不存在：ordersId=" + ordersId);
            }
            if (beatOrdersInfoDto.getBeatInto().equals("1")) {
                return AjaxResult.error("此商品已捶成", ShowTypeEnum.用户.getValue());
            }
            if (beatOrdersInfoDto.getBeatInto().equals("0")) {
                return AjaxResult.error(502,"此商品捶一捶已结束", ShowTypeEnum.用户.getValue());
            }
            //获取订单剩余金额集合
            List<String> random = redisCache.getCacheList("ORDERS_ID:" + ordersId);
            //获取总金额
            BigDecimal goodsPrice = redisCache.getCacheObject("ORDERS_PRICE:" + ordersId);
            BigDecimal price = new BigDecimal("0");
            //计算剩余总金额
            for (String s : random) {
                price = price.add(new BigDecimal(s));
            }
            BigDecimal percent = price.divide(goodsPrice, 1, BigDecimal.ROUND_HALF_UP);
            beatOrdersInfoDto.setPercent(random.size() == 0 ? (0 + "") : percent + "");
            beatOrdersInfoDto.setRemainAmount(random.size() == 0 ? (0 + "") : price + "");
            //帮捶记录
            List<HelpRecordDto> helpRecordDtoList = redisCache.getCacheList("ORDERS_HELP:" + ordersId);
            //查询用户是否帮捶过
            if (helpRecordDtoList != null && helpRecordDtoList.size() > 0) {
                for (HelpRecordDto helpRecordDto : helpRecordDtoList) {
                    if (helpRecordDto.getUserId().equals(ApiUtils.getUserId())) {
                        //默认为false，捶过设置
                        beatOrdersInfoDto.setIsHelp(true);
                    }
                }
            }
            return AjaxResult.success(beatOrdersInfoDto);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("获取捶一捶订单信息" + e.getMessage());
            return AjaxResult.error(e.getMessage());
        }

    }

    @ApiOperation("获取砍价记录")
    @GetMapping("/gethelpRecords")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = Constants.TOKEN, value = "token标记", required = true),
            @ApiImplicitParam(paramType = "ordersId", value = "订单id", dataType = "Long", required = true),
    })
    @LoginRequired(role = ApiUtils.DEFAULT_ROLE_USER)
    public AjaxResult gethelpRecords(Long ordersId) {
        try {
            List<HashMap<String, String>> data = redisCache.getCacheList("ORDERS_HELP:" + ordersId);
            if (data == null) {
                data = new ArrayList<>();
            }
            return AjaxResult.success(data);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("获取砍价记录" + e.getMessage());
            return AjaxResult.error(e.getMessage());
        }
    }

    @ApiOperation("获取所有最新砍价记录")
    @GetMapping("/getAllNewhelpRecords")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = Constants.TOKEN, value = "token标记", required = true),
            @ApiImplicitParam(paramType = "beatInto", value = "是否捶成 1=捶成 0=未捶成 2=进行中", dataType = "String", required = true),
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "", response = BeatNewsDto.class)
    })
    public AjaxResult getAllNewhelpRecords(String beatInto) {
        try {
            startPage();
            List<BeatNewsDto> beatNewsDtos = userBeatService.selectUserBeatSuccessList(beatInto);
            return AjaxResult.success(beatNewsDtos);
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.error(e.getMessage());
        }
    }


    //添加帮捶记录
    @Transactional
    public void helpRecord(SysUser user,UserBeat userBeat, String beatPrice, Date date) {
        if(null==userBeat||null==userBeat.getOrdersId()){
            throw new CustomException("此捶一捶订单不存在",ShowTypeEnum.用户.getValue());
        }
        List<HelpRecordDto> data = redisCache.getCacheList("ORDERS_HELP:" + userBeat.getOrdersId());

        HelpRecordDto helpRecordDto = new HelpRecordDto();
        helpRecordDto.setOrdersId(userBeat.getOrdersId());
        helpRecordDto.setUserId(user.getUserId());
        helpRecordDto.setUserName(user.getUserName());
        helpRecordDto.setAvatar(user.getAvatar());
        helpRecordDto.setBeatPrice(beatPrice);
        helpRecordDto.setBeatTime(DateUtils.getNowDate());
        //是否帮捶自己
        boolean isSelf=ApiUtils.getUserId().compareTo(userBeat.getUserId())==0;
        helpRecordDto.setIsSelf(isSelf?1:0);
        userBeatHelpService.insertUserBeatHelp(helpRecordDto);

        //不存在，新增
        if (data == null) {

            data = new ArrayList<>();
            data.add(helpRecordDto);

            helpRecordCache("ORDERS_HELP:" + userBeat.getOrdersId(), data, date);
        } else {
            data.add(helpRecordDto);
            helpRecordCache("ORDERS_HELP:" + userBeat.getOrdersId(), data, date);
        }
    }

    //添加帮捶缓存
    public void helpRecordCache(String key, List<HelpRecordDto> data, Date date) {
        //删除
        redisCache.deleteObject(key);
        //添加评价
        redisCache.setCacheList(key, data);
        //设置过期时间
        redisTemplate.expireAt(key, date);
    }


}


